﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BudgetRight.Linq
{
    public static class LinqExtensions
    {
        /// <summary>
        /// Performs the specified action on each element of the System.Collections.Generic.List<T>.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="action">The System.Action<T> delegate to perform on each element of the System.Collections.Generic.List<T>.</param>
        public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            foreach (var item in source)
            {
                action(item);
            }
        }

        /// <summary>
        /// Synchronizes the first collection to look like the second.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="first"></param>
        /// <param name="second"></param>
        /// <param name="keySelector"></param>
        public static void Synchronize<TSource, TKey>(this ICollection<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector)
        {
            if (first == null)
                throw new ArgumentNullException("first");

            if (second == null)
                throw new ArgumentNullException("second");

            if (keySelector == null)
                throw new ArgumentNullException("keySelector");

            first.Except(second, keySelector).ToList().ForEach(a => first.Remove(a));
            second.Except(first, keySelector).ToList().ForEach(first.Add);
        }

        /// <summary>
        /// Produces the set difference of two sequences by using the default equality comparer to compare values.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements of the input sequences.</typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="first">An System.Collections.Generic.IEnumerable<T> whose elements that are not also in second will be returned.</param>
        /// <param name="second">An System.Collections.Generic.IEnumerable<T> whose elements that also occur in the first sequence will cause those elements to be removed from the returned sequence.</param>
        /// <param name="keySelector"></param>
        /// <returns>A sequence that contains the set difference of the elements of two sequences.</returns>
        public static IEnumerable<TSource> Except<TSource, TKey>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, TKey> keySelector)
        {
            if (first == null)
                throw new ArgumentNullException("first");

            if (second == null)
                throw new ArgumentNullException("second");

            if (keySelector == null)
                throw new ArgumentNullException("keySelector");

            var results = from firstItem in first
                          where !(from secondItem in second
                                  select keySelector(secondItem)).Contains(keySelector(firstItem))
                          select firstItem;

            return results;
        }
    }
}
